#! /bin/bash

#  This script is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License version 2 as
#  published by the Free Software Foundation.
#
#  See the COPYING and AUTHORS files for more details.

# Read in library functions
if [ "$(type -t patch_file_name)" != function ]
then
	if ! [ -r $CMDPATH/scripts/patchfns ]
	then
		echo "Cannot read library $CMDPATH/scripts/patchfns" >&2
		exit 1
	fi
	. $CMDPATH/scripts/patchfns
fi

usage()
{
	printf $"Usage: quilt pop [-afRqv] [num|patch]\n"
	if [ x$1 = x-h ]
	then
		printf $"
Remove patch(es) from the stack of applied patches.  Without options,
the topmost patch is removed.  When a number is specified, remove the
specified number of patches.  When a patch name is specified, remove
patches until the specified patch end up on top of the stack.  Patch
names may include the patches/ prefix, which means that filename
completion can be used.

-a	Remove all applied patches.

-f	Force remove. The state before the patch(es) were applied will
	be restored from backup files.

-R	Always verify if the patch removes cleanly; don't rely on
	timestamp checks.

-q	Quiet operation.

-v	Verbose operation.
"
		exit 0
	else
		exit 1
	fi
}

list_patches()
{
	local n=0 patch
	applied_patches \
	| tac \
	| if [ -n "$opt_all" ]
	then
		cat
	else
		while read patch
		do
			if [ -n "$number" ]
			then
				if [ $n -eq $number ]
				then
					break
				fi
				n=$[$n+1]
			fi
			if [ $patch = "$stop_at_patch" ]
			then
				break
			fi
			echo $patch
		done
	fi
}

files_may_have_changed()
{
	local patch=$1 file
	local patch_file=$(patch_file_name $patch)
	
	if [ $? -ne 0 -o ! -e "$patch_file" \
	     -o ! -e "$QUILT_PC/$patch/.timestamp" \
	     -o "$QUILT_PC/$patch/.timestamp" -ot "$patch_file" ]
	then
		return 0
	fi
	
	local apply_ts=$(date -r "$QUILT_PC/$patch/.timestamp" '+%s') ts
	for file in $(files_in_patch $patch)
	do
		ts=$(date -r $file '+%s' 2> /dev/null)
		[ -z "$ts" ] && return 0
		[ "$ts" -gt $apply_ts ] && return 0
	done
	return 1
}

# Check if all changes have been folded back into the patch (quilt refresh),
# and report any pending changes.
check_for_pending_changes()
{
	local patch=$1
	local patch_file=$(patch_file_name $patch)
	local workdir=$(gen_tempfile -d quilt) status=0

	if [ -d $QUILT_PC/$patch ]
	then
		if ! rmdir $workdir ||  # note that this is racey...
		   ! cp -rl $QUILT_PC/$patch $workdir
		then
			printf $"Failed to copy files to temporary directory\n" >&2
			rm -rf $workdir
			return 1
		fi

		# Now we may have some zero-size files that have no
		# permissions (which represent files that the patch
		# creates). Those may have been created in the meantime,
		# but patch would refuse to touch them: We must remove
		# them here.
		find $workdir -type f -size 0 -exec rm -f '{}' ';'

	fi
	
	if [ -s $patch_file ]
	then
		if ! cat_file $patch_file \
		     | /usr/bin/patch -d $workdir $QUILT_PATCH_OPTS \
			       $(patch_args $patch) \
			       --no-backup-if-mismatch -E \
			       >/dev/null 2>/dev/null
		then
			if ! [ -e $QUILT_PC/$patch ]
			then
				printf $"Failed to patch temporary files\n" >&2
				rm -rf $workdir
				return 1
			fi
		fi
	fi
	
	local file failed
	for file2 in $(files_in_patch $patch)
	do
		file=$workdir/$file2
		[ -e $file  ] || file=/dev/null
		[ -e $file2 ] || file2=/dev/null
		/usr/bin/diff -q $file $file2 > /dev/null || failed=1
	done
	
	if [ -n "$failed" ]
	then
		printf $"Patch %s does not remove cleanly (refresh it or enforce with -f)\n" \
		       "$(print_patch $patch)" >&2
		status=1
	fi
	rm -rf $workdir

	return $status
}

remove_patch()
{
	local patch=$1 status=0

	trap "status=1" SIGINT
	if [ -z "$opt_force" ] && \
	   ( [ -n "$opt_remove" ] || files_may_have_changed $patch )
	then
		check_for_pending_changes $patch || status=1
	fi

	if [ $status -eq 0 ]
	then
		rm -f "$QUILT_PC/$patch/.timestamp"
		if [ -z "$(shopt -s nullglob ; echo "$QUILT_PC/$patch/"*)" ]
		then
			printf $"Patch %s appears to be empty, removing\n" \
			       "$(print_patch $patch)"
			status=0
		else
			printf $"Removing patch %s\n" "$(print_patch $patch)"
			/usr/lib64/quilt/backup-files $silent -r -t -B $QUILT_PC/$patch/ -
			status=$?
		fi
		remove_from_db $patch
		rm -f $QUILT_PC/$patch~refresh
	fi
	trap - SIGINT
	return $status
}

options=`getopt -o fRqvah -- "$@"`

if [ $? -ne 0 ]
then
        usage
fi

eval set -- "$options"

while true
do
        case "$1" in
        -f)
                opt_force=1
		unset opt_remove
		shift ;;
	-R)
		opt_remove=1
		unset opt_force
		shift ;;
        -q)
                opt_quiet=1
		shift ;;
        -v)
                opt_verbose=1
		shift ;;
	-a)
		opt_all=1
		shift ;;
	-h)
		usage -h ;;
        --)
                shift
		break ;;
        esac
done

if [ $# -gt 1 -o \( -n "$opt_all" -a $# -ne 0 \) ]
then
        usage
fi

if [ $# -eq 1 ]
then
	if is_numeric $1
	then
		number=$1
	else
		if ! stop_at_patch=$(find_patch $1)
		then
			printf $"Patch %s is not in series\n" "$1" >&2
			exit 1
		fi
	fi
else
	[ -n "$opt_all" ] || number=1
fi

[ -n "$opt_quiet" ] && silent=-s
[ -z "$opt_verbose" ] && silent_unless_verbose=-s

if [ -n "$stop_at_patch" ]
then
	if ! is_applied $stop_at_patch
	then
		printf $"Patch %s is not applied\n" "$(print_patch $stop_at_patch)" >&2
		exit 1
	fi
fi

top=$(top_patch)
if [ -n "$top" -a -e $QUILT_PC/$top~refresh -a -z "$opt_force" ]
then
	printf $"Patch %s needs to be refreshed first.\n" \
	       "$(print_patch $top)" >&2
	exit 1
fi

if ! patches=$(list_patches) 2>&1
then
	exit 1
elif [ -z "$patches" ]
then
        printf $"No patch removed\n" >&2
	exit 2
fi

for patch in $patches
do
	if ! remove_patch $patch
	then
		exit 1
	fi
	[ -z "$opt_quiet" ] && echo
done

patch="$(top_patch)"
if [ -z "$patch" ]
then
	printf $"No patches applied\n"
else
	# Ensure that the files in the topmost patch have a link count
	# of one: This will automatically be the case in the usual
	# situations, but we don't want to risk file corruption in weird
	# corner cases such as files added to a patch but not modified.
	/usr/lib64/quilt/backup-files -L -s -B $QUILT_PC/$patch/ -
	printf $"Now at patch %s\n" "$(print_patch $patch)"
fi
### Local Variables:
### mode: shell-script
### End:
# vim:filetype=sh
